#ifndef ELF_H
#define ELF_H

/**
	Sollten Fragen zum ELF Dateiformat bestehen, die nicht durch
	die zahlreichen Erklärungen gedeckt sind, bitte ich die
	offizielle ELF Spezifikation zu lesen.
**/

#include "main.h"
#include "settings.h"
#include "structs.h"
#include "print.h"
#include "memory.h"
#include "panic.h"
#include "paging.h"
#include "string.h"
#include "scheduler.h"
#include "fs.h"

// Makros für Realloc
#define ELF32_R_SYM(i)	((i)>>8)
#define ELF32_R_TYPE(i)	((uint8_t)(i))
#define ELF32_R_INFO(s,t) (((s)<<8)+(uint8_t)(t))

// Dateitypen
#define ET_NONE 0		// Keine ELF Datei
#define ET_REL 1		// Dynamisch verschiebbare Datei
#define ET_EXEC 2		// Ausführbare Datei
#define ET_DYN 3		// Dynamische Datei (".so")
#define ET_CORE 4		// Core Datei
#define ET_LOPROC 0xFF00	// Prozessor spezifisch (???)
#define ET_HIPROC 0xFFFF	// Prozessor spezifisch (???)

// Architekturen
#define EM_NONE 0	// Keine Architektur
#define EM_M32 1	// AT&T WE 32100
#define EM_SPARC 2	// SPARC
#define EM_386 3	// Intel 80386
#define EM_64K 4	// Motorola 68000
#define EM_88K 5	// Motorola 88000
#define EM_860 7	// Intel 80860
#define EM_MIPS 8	// MIPS RS3000

// Versionen
#define EV_NONE 0	// Ungültige Version
#define EV_CURRENT 1	// Aktuelle Version

// Dateiklassen
#define ELFCLASSNONE	0	// Ungültige Klasse
#define ELFCLASS32	1	// 32Bit Code
#define ELFCLASS64	2	// 64bit Code

// Endians
#define ELFDATANONE	0	// Ungültig
#define ELFDATA2LSB	1	// Little Endian
#define ELFDATA2MSB	2	// Big Endian

// Einträge in e_ident
#define EI_NIDENT	16	// Anzahl der Einträge
#define EI_MAG0		0	// Magic 1
#define EI_MAG1		1	// Magic 2
#define EI_MAG2		2	// Magic 3
#define EI_MAG3		3	// Magic 4
#define EI_CLASS	4	// Dateiklasse
#define EI_DATA		5	// Endianness
#define EI_VERSION	6	// Version der ELF Datei - Muss EV_CURRENT sein
#define EI_PAD		7	// Anzahl der unbenutzten Bytes in e_ident

// Reservierte Einträge in der Section Header Table
#define SHN_UNDEF	0	// Dieser Wert ist nich definiert
#define SHN_LORESERVE	0xFF00	// Von diesem Wert an bis SHN_HIRESERVE sind alle Einträge reserviert
#define SHN_LOPROC	0xFF00	// Prozessor spezifische Sachen
#define SHN_HIPROC	0xFF1F	// Siehe SHN_LOPROC
#define SHN_ABS		0xFFF1	// Werte sind absolut und sollen nicht verschoben werden
#define SHN_COMMON	0xFFF2	// Werte wie zB externe Variablen in C
#define SHN_HIRESERVE	0xFFFF	// Siehe SHN_LORESERVE

// Typen für einen Sektionsheader (sh_type)
#define SHT_NULL	0		// Die Sektion ist unbenutzt
#define SHT_PROGBITS	1		// Die Sektion enthält Daten, die vom Programm definiert sind
#define SHT_SYMTAB	2		// Die Sektion enthält eine Symbol Tabelle
#define SHT_STRTAB	3		// Die Sektion enthält eine String Tabelle
#define SHT_RELA	4		// Die Sektion enthält Reallokations Informationen
#define SHT_HASH	5		// Die Sektion enthält eine Symbol Hash Tabelle
#define SHT_DYNAMIC	6		// Die Sektion enthält Informationen für dynamisches Linken
#define SHT_NOTE	7		// Die Sektion enthält Notizen (vom Linker?)
#define SHT_NOBITS	8		// Die Sektion ist nur ein Platzhalter und belegt keinen Speicher
#define SHT_REL		9		// Die Sektion enthält Reallokations Informationen ohne explizite Angaben
#define SHT_SHLIB	10		// Reservierte ID. Programme, die solch ein Tabelle enthalten sind nicht konform
#define SHT_DYNSYM	11		// Siehe SHT_SYMTAB
#define SHT_LOPROC	0x7000000	// Unteres Ende für Programm spezifische Sektionen
#define SHT_HIPROC	0x7FFFFFF	// Oberes Ende für Programm spezifische Sektionen
#define SHT_LOUSER	0x8000000	// -"-
#define SHT_HIUSER	0xFFFFFFF	// -"-

// Bitflags für sh_flags
#define SHF_WRITE	0x01		// Die Sektion ist vom Programm beschreibbar
#define SHF_ALLOC	0x02		// Die Sektion belegt Speicher während der Ausführung
#define SHF_EXECINSTR	0x04		// Die Sektion enthält ausführbaren Maschinencode
#define SHF_MASKPROC	0xF0000000	// Reserviert für Architektur abhängige Sachen

// Segment Typen (p_type)
#define PT_NULL		0		// Unbenutzt
#define PT_LOAD		1		// Ladbares Segment. Wenn p_memsz > p_filesz, dann werden die restlichen Bytes mit 0 aufgefüllt
#define PT_DYNAMIC	2		// Dynamisch linkbares Segment
#define PT_INTERP	3		// Zeigt auf einen Null-terminierten String für einen Interpreten (!?)
#define PT_NOTE		4		// Zeigt auf ein Note Segment
#define PT_SHLIB	5		// Programme, die solch ein Segment enthalten sind nicht konform
#define PT_PHDR		6		// Enthält die Position und die Größe der Program Header Table
#define PT_LOPROC	0x70000000	// 
#define PT_HIPROC	0x7FFFFFFF	// 

// Einstellungen für die i386 Architektur
#define ELF_ARCH_CLASS	ELFCLASS32	// Wir wollen 32bit Code
#define ELF_ARCH_DATA	ELFDATA2LSB	// Little Endian
#define ELF_ARCH_IDENT	EM_386		// i386 Architektur

// Header einer ELF Datei
typedef struct {
	uint8_t		e_ident[EI_NIDENT];	// Enthält Architektur Unabhängige Informationen
	Elf32_Half	e_type;			// Genauer Dateityp (siehe ET_...)
	Elf32_Half	e_machine;		// Architektur (siehe EM_...)
	Elf32_Word	e_version;		// Version der ELF Datei
	Elf32_Addr	e_entry;		// Einsprungspunkt
	Elf32_Off	e_phoff;		// Enthält die Position der Program Header Table oder 0 wenn es keine gibt
	Elf32_Off	e_shoff;		// Enthält die Position der Section Header Table oder 0 wenn es keine gibt
	Elf32_Word	e_flags;		// Enthält Architektur abhängige Flags
	Elf32_Half	e_ehsize;		// Größe des ELF Headers
	Elf32_Half	e_phentsize;		// Größe eines Eintrags in der Program Header Table
	Elf32_Half	e_phnum;		// Anzahl der Einträge in der Program Header Table
	Elf32_Half	e_shentsize;		// Größe eines Eintrags eines Sektions Headers
	Elf32_Half	e_shnum;		// Anzahl der Einträge in der Section Header Table
	Elf32_Half	e_shstrndx;		// Eintrag in der Section Header Table, der auf die Section Name Table zeigt. Wenn es keine gibt ist dieser Eintrag SHN_UNDEF
} __attribute__ ((packed)) Elf32_Ehdr_t;

// Section Header
typedef struct {
	Elf32_Word	sh_name;	// Name der Sektion. Zeigt auf einen Eintrag in der Section Header String Table
	Elf32_Word	sh_type;	// Typ des Eintrags
	Elf32_Word	sh_flags;	// 1bit Flags
	Elf32_Addr	sh_addr;	// Adresse der Sektion im RAM. Wenn 0 dann exestiert diese Sektion für das Programm nicht
	Elf32_Off	sh_offset;	// Adresse der Sektion in der ELF Datei
	Elf32_Word	sh_size;	// Größe der Sektion. Wenn SHT_NOBITS gesetzt ist belegt die Sektion keinen Speicher
	Elf32_Word	sh_link;	// Zeigt auf einen Section Header Table Eintrag
	Elf32_Word	sh_info;	// Von dem Sektionstyp abhängige Informationen
	Elf32_Word	sh_addralign;	// Sektions alignment. Die Werte 0 und 1 stehen für kein Alignment 
	Elf32_Word	sh_entsize;	// Enthält die Größe von Einträgen mit fester Größe. Wenn 0 dann gibt es in der Sektion keiner solcher Einträge
} __attribute__ ((packed)) Elf32_Shdr_t;

// Symbol Table Entry
typedef struct {
	Elf32_Word	st_name;	// 
	Elf32_Addr	st_value;	// 
	Elf32_Word	st_size;	// 
	uint8_t		st_info;	// 
	uint8_t		st_other;	// 
	Elf32_Half	st_shndx;	// 
} __attribute__ ((packed)) Elf32_Sym_t;

// Realloc
typedef struct {
	Elf32_Addr	r_offset;	// Gibt die Position in der Sektion an, die betroffen ist
	Elf32_Word	r_info;		// 
} __attribute__ ((packed)) Elf32_Rel_t;

typedef struct {
	Elf32_Addr	r_offset;	// 
	Elf32_Word	r_info;		// 
	Elf32_Sword	r_addend;	// Summand, der auf die Adresse addiert wird
} __attribute__ ((packed)) Elf32_Rela_t;

// Program Header
typedef struct {
	Elf32_Word	p_type;		// Typ
	Elf32_Off	p_offset;	// Zeigt auf die Startadresse eines Segments
	Elf32_Addr	p_vaddr;	// Zeigt auf die Startadresse im Virtuellen Adresseraum
	Elf32_Addr	p_paddr;	// Für uns nicht relevant
	Elf32_Word	p_filesz;	// Größe des Segments in der Datei, kann 0 sein
	Elf32_Word	p_memsz;	// Größe des Segments im RAM, kann 0 sein
	Elf32_Word	p_flags;	// Flags
	Elf32_Word	p_align;	// Alignment des Segments. 0 und 1 = Kein Alignment
} __attribute__ ((packed)) Elf32_Phdr_t;

/** Prototypen **/

/* Lädt eine ELF Datei in den Speicher und richtet diese ein
 * Parameter: Pfad zur ELF Datei
 * Rückgaben: Prozess ID
 */
pid_t elf_load (const char *path);

/* Erstellt einen Task aus einer Programm Header Table - NOTIZ: Der Task läuft noch nicht sondern muss noch aktiviert werden!
* Parameter: Zeiger auf einen Elf Header, Zeiger auf eine Elf32_Phdr_t Struktur
* Rückgaben: Bei Erfolg Zeiger auf eine Task Struktur, ansonsten NULL
*/
struct task *elf_create_task (Elf32_Ehdr_t *, Elf32_Phdr_t *);

/* Dies ist der Einsprungspunkt eines ELF Programms.
* Parameter: Adresse des Einsprungspunkts (Wird übern Stack übergeben, siehe elf_create_task)
* Rückgaben: Keine
*/
void elf_entrypoint (int (*entry)(int, char**));

/** 
	Sections Header Table Entry Index 0:
	Feld		Wert		Beschreibung
	============================================================
	sh_name		0		Kein Name
	sh_type		SHT_NULL	Inaktiv
	sh_flags	0		Keine Flags
	sh_addr		0		Keine Adresse
	sh_offset	0		Keine Position in der ELF Datei
	sh_size		0		Größe = 0Bytes
	sh_link		SHN_UNDEF	Keine Linker Informationen
	sh_info		0		Keine weiteren Informationen
	sh_addralign	0		Kein Alignment
	sh_entsize	0		Enthält keine Einträge fester Größe
**/

/**
	Besondere Sektionen
	Name		Typ		Attribute
	=============================================================
	.bss		SHT_NOBITS	SHF_ALLOC & SHF_WRITE
	.comment	SHT_PROGBITS	KEINE
	.data		SHT_PROGBITS	SHF_ALLOC & SHF_WRITE
	.data1		SHT_PROGBITS	SHF_ALLOC & SHF_WRITE
	.debug		SHT_PROGBITS	KEINE
	.dynamic	SHT_DYNAMIC	
	.dynstr		SHT_STRTAB	SHF_ALLOC
	.dynsym		SHT_DYNSYM	SHF_ALLOC
	.fini		SHT_PROGBITS	SHF_ALLOC + SHF_EXECINSTR
	.got		SHT_PROGBITS	
	.hash		SHT_HASH	SHF_ALLOC
	.init		SHT_PROGBITS	SHF_ALLOC & SHF_EXECINSTR
	.interp		SHT_PROGBITS	
	.line		SHT_PROGBITS	KEINE
	.note		SHT_NOTE	KEINE
	.plt		SHT_PROGBITS	
	.rel[name]	SHT_REL		
	.rela[name]	SHT_RELA	
	.rodata		SHT_PROGBITS	SHF_ALLOC
	.rodata1	SHT_PROGBITS	SHF_ALLOC
	.shstrtab	SHT_STRTAB	KEINE
	.strtab		SHT_STRTAB	
	.symtab		SHT_SYMTAB	
	.text		SHT_PROGBITS	SHF_ALLOC & SHF_EXECINSTR
**/

#endif
